Buy Access to Course
31.

Campos totalmente personalizados

|

Share this awesome video!

|

Keep on Learning!

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

Login Subscribe

Pongámonos salvajes. Quiero añadir un nuevo campo totalmente personalizado y loco a nuestra API DragonTreasure que no se corresponda con ninguna propiedad de nuestra clase. Bueno, en realidad, en la parte 1 de esta serie aprendimos que es posible añadir campos personalizados creando un método getter y añadiendo un grupo de serialización sobre él. Pero esa solución sólo funciona si podemos calcular el valor del campo únicamente a partir de los datos del objeto. Si, por ejemplo, necesitamos llamar a un servicio para obtener los datos, entonces no tendremos suerte.

Añadir un nuevo campo cuyos datos se calculen a partir de un servicio es otro as en la manga del normalizador personalizado. Y como ya tenemos uno configurado, he pensado que podríamos utilizarlo para ver cómo funciona.

Prueba del campo IsMe

Ve a DragonTreasureResourceTest y buscatestOwnerCanSeeIsPublishedField(). Cámbiale el nombre atestOwnerCanSeeIsPublishedAndIsMineFields():

// ... lines 1 - 12
class DragonTreasureResourceTest extends ApiTestCase
{
// ... lines 15 - 158
public function testOwnerCanSeeIsPublishedAndIsMineFields(): void
{
// ... lines 161 - 178
}
}

Esto es un poco tonto, pero si tenemos un DragonTreasure, vamos a añadir una nueva propiedad booleana llamada $isMine establecida en true. Así que, abajo del todo, diremos isMine y esperaremos que sea true:

// ... lines 1 - 12
class DragonTreasureResourceTest extends ApiTestCase
{
// ... lines 15 - 158
public function testOwnerCanSeeIsPublishedAndIsMineFields(): void
{
// ... lines 161 - 166
$this->browser()
// ... lines 168 - 175
->assertJsonMatches('isPublished', false)
->assertJsonMatches('isMine', true)
;
}
}

Copia ese nombre de método, luego gira y ejecuta esta prueba:

symfony php bin/phpunit --filter=testOwnerCanSeeIsPublishedAndIsMineFields

¡Tada! Es null porque el campo aún no existe.

Devolver el campo personalizado

¿Cómo podemos añadirlo? Ahora que hemos pasado por el engorro de configurar el normalizador, ¡es fácil! El sistema normalizador hará lo suyo, devolverá los datos normalizados y luego, entre eso y la declaración return, podemos... ¡joder!

// ... lines 1 - 12
class AddOwnerGroupsNormalizer implements NormalizerInterface, SerializerAwareInterface
{
// ... lines 15 - 18
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
if ($object instanceof DragonTreasure && $this->security->getUser() === $object->getOwner()) {
$context['groups'][] = 'owner:read';
}
$normalized = $this->normalizer->normalize($object, $format, $context);
// ... lines 26 - 30
return $normalized;
}
// ... lines 33 - 44
}

Copia la sentencia if de aquí arriba. Podría ser más inteligente y reutilizar código, pero está bien. Si el objeto es un DragonTreasure y poseemos esteDragonTreasure, diremos $normalized['isMine'] = true:

// ... lines 1 - 12
class AddOwnerGroupsNormalizer implements NormalizerInterface, SerializerAwareInterface
{
// ... lines 15 - 18
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
// ... lines 21 - 24
$normalized = $this->normalizer->normalize($object, $format, $context);
if ($object instanceof DragonTreasure && $this->security->getUser() === $object->getOwner()) {
$normalized['isMine'] = true;
}
return $normalized;
}
// ... lines 33 - 44
}

¡Ya está! Cuando ejecutemos la prueba:

symfony php bin/phpunit --filter=testOwnerCanSeeIsPublishedAndIsMineFields

¡Todo verde!

Campos personalizados que faltan en los documentos

Pero estos campos personalizados tienen un inconveniente práctico: no estarán documentados en nuestra API. ¡Nuestros documentos de la API no tienen ni idea de que esto existe!

Si necesitas un supercampo personalizado que requiera lógica de servicio... y necesitas que esté documentado, tienes dos opciones. En primer lugar, podrías añadir una propiedad no persistente isMe a tu clase y luego rellenarla con un proveedor de estado. Aún no hemos hablado de los proveedores de estado, pero son la forma en que se cargan los datos. Por ejemplo, nuestras clases ya utilizan un proveedor de estado Doctrine entre bastidores para consultar la base de datos. Hablaremos de los proveedores de estado en la parte 3 de esta serie.

La segunda solución sería utilizar el normalizador personalizado como hicimos nosotros, y luego intentar añadir el campo a los documentos OpenAPI manualmente mediante el truco de la fábrica OpenAPI que mostramos antes.

A continuación: supongamos que un usuario tiene permiso para editar algo... pero hay ciertos cambios en los datos que no puede hacer -por ejemplo, podría establecer un campo enfoo pero no puede cambiarlo a bar porque no tiene suficientes permisos. ¿Cómo debemos manejar esto? Es la seguridad unida a la validación.